#include <app_pulse_sensor.h>
/*
 * Copyright (c) 2006-2021, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2022-06-19     hehung       the first version
 */
#include <rtdevice.h>
#include <rtthread.h>
#include "app_common.h"
#include "app_adc.h"
#include "gd32f4xx.h"
#include "lvgl.h"

/****************************************************************
  * 术语：Pluse Sensor - PS - 心率传感器
  * 功能：该模块为心率监测模块的驱动代码，使用ADC采集心率信息并通过
  * 算法解析出来当前的心率数据，定时器实现周期性采集ADC
  * 使用引脚：PA1
 ***************************************************************/
#define PS_REFER_VOLTAGE                    (330U)         /* 参考电压 3.3V,数据精度乘以100保留2位小数*/

#define THREAD_STACK_SIZE_PS                (1024*5U)
#define THREAD_PRIORITY_PS                  (3U)

#define PS_DEBUG                            (COMMON_OFF)


static rt_thread_t thread_ps_hd = RT_NULL;


//心率采集相关变量
int BPM;                            //脉搏率==就是心率
int Signal;                         //传入的原始数据。
int IBI = 600;                      //节拍间隔，两次节拍之间的时间（ms）。计算：60/IBI(s)=心率（BPM）
bool Pulse = false;        //脉冲高低标志。当脉波高时为真，低时为假。
bool QS = false;           //当发现一个节拍时，就变成了真实。
int rate[10];                       //数组保存最后10个IBI值。
uint32_t sampleCounter = 0;    //用于确定脉冲定时。
uint32_t lastBeatTime = 0;     //用于查找IBI
int P = 512;                         //用于在脉冲波中寻找峰值
int T = 512;                        //用于在脉冲波中寻找波谷
int thresh = 512;                   //用来寻找瞬间的心跳
int amp = 100;                      //用于保持脉冲波形的振幅
int Num;
uint8_t firstBeat = true;     //第一个脉冲节拍
uint8_t secondBeat = false;   //第二个脉冲节拍，这两个变量用于确定两个节拍

#if (COMMON_USE_LVGL == COMMON_OFF)
uint16_t ps_waveformlist[PS_WAVE_POINT_NUM] = {0, 0};
#else
/* ADC采样值保留用于显示 */
lv_point_t ps_waveformlist[PS_WAVE_POINT_NUM] = {0, 0};
#endif

static void Ps_Thread_Entry(void *para);
static void Ps_HeartRateDeal(void);
static void Ps_ReadSampleValueFromAdc(void);
static void Ps_WaveformDeal(uint16_t adc_value);

void Ps_TaskCreate(void)
{
    thread_ps_hd = rt_thread_create("thread_ps",
                                    Ps_Thread_Entry,
                                    RT_NULL,
                                    THREAD_STACK_SIZE_PS,
                                    THREAD_PRIORITY_PS,
                                    10);
    if (RT_NULL != thread_ps_hd)
    {
        rt_thread_startup(thread_ps_hd);
    }
}

void Ps_TaskDelete(void)
{
    rt_thread_delete(thread_ps_hd);
}

/* Pluse sensor任务入口 */
static void Ps_Thread_Entry(void *para)
{
#if (PS_DEBUG == COMMON_ON)
    uint32_t cnt = 0;
#endif

    while (1)
    {
#if (PS_DEBUG == COMMON_ON)
        if (cnt == 50)
        {
            cnt = 0;
            rt_kprintf("\nIBI = %d, BPM = %d, Adc = %d", IBI, BPM, Signal);
        }
        else
        {
           cnt ++;
        }
#endif
        Ps_WaveformDeal(Signal);
        rt_thread_mdelay(20);
    }
}

/* 阅读采集自心率传感器的ADC值 */
static void Ps_ReadSampleValueFromAdc(void)
{
    rt_uint32_t value;

    /* 读取采样值 */
    value = Adc_ChSample(ADC_CHANNEL_1);

    /* 转换采集到的ADC值的精度为10bits */
    Signal = value>>2;
}

/* 采集心率信号的处理函数 */
static void Ps_HeartRateDeal(void)
{
    unsigned int runningTotal;
    uint8_t i;

    sampleCounter += 2;
    Num = sampleCounter - lastBeatTime;             //监控最后一次节拍后的时间，以避免噪声

    //找到脉冲波的波峰和波谷
    if((Signal < thresh) && (Num > (IBI/5)*3))  //为了避免需要等待3/5个IBI的时间
    {
        if(Signal < T)
        {                                       //T是阈值
            T = Signal;                         //跟踪脉搏波的最低点，改变阈值
        }
    }
    if((Signal > thresh) && (Signal > P))       //采样值大于阈值并且采样值大于峰值
    {
        P = Signal;                             //P是峰值，改变峰值
    }
    //现在开始寻找心跳节拍
    if (Num > 250)              //避免高频噪声
    {
        if ((Signal > thresh) && (Pulse == false) && (Num > (IBI/5)*3))
        {
            Pulse = true;                               //当有脉冲的时候就设置脉冲信号。
            IBI = sampleCounter - lastBeatTime;         //测量节拍的ms级的时间
            lastBeatTime = sampleCounter;               //记录下一个脉冲的时间。
            if(secondBeat)          //如果这是第二个节拍，如果secondBeat == TRUE，表示是第二个节拍
            {
                secondBeat = false;                  //清除secondBeat节拍标志
                for(i=0; i<=9; i++)     //在启动时，种子的运行总数得到一个实现的BPM。
                {
                    rate[i] = IBI;
                }
            }
            if(firstBeat)           //如果这是第一次发现节拍，如果firstBeat == TRUE。
            {
                firstBeat = false;                   //清除firstBeat标志
                secondBeat = true;                   //设置secongBeat标志
                return;                              //IBI值是不可靠的，所以放弃它。
            }
            //保留最后10个IBI值的运行总数。
            runningTotal = 0;                  //清除runningTotal变量

            for(i=0; i<=8; i++)             //转换数据到rate数组中
            {
                rate[i] = rate[i+1];                  //去掉旧的的IBI值。
                runningTotal += rate[i];              //添加9个以前的老的IBI值。
            }

            rate[9] = IBI;                          //将最新的IBI添加到速率数组中。
            runningTotal += rate[9];                //添加最新的IBI到runningTotal。
            runningTotal /= 10;                     //平均最后10个IBI值。
            BPM = 60000/runningTotal;               //一分钟有多少拍。即心率BPM
            if(BPM>200)
                BPM=200;         //限制BPM最高显示值
            if(BPM<30)
                BPM=30;               //限制BPM最低显示值
            QS = true;
        }
    }

    if (Signal < thresh && Pulse == true)       //当值下降时，节拍就结束了。
    {
        Pulse = false;                         //重设脉冲标记，这样方便下一次的计数
        amp = P - T;                           //得到脉冲波的振幅。
        thresh = amp/2 + T;                    //设置thresh为振幅的50%。
        P = thresh;                            //重新设置下一个时间
        T = thresh;
    }

    if (Num > 2500)             //如果2.5秒过去了还没有节拍
    {
        thresh = 512;                          //设置默认阈值
        P = 512;                               //设置默认P值
        T = 512;                               //设置默认T值
        lastBeatTime = sampleCounter;          //把最后的节拍跟上来。
        firstBeat = true;                      //设置firstBeat为true方便下一次处理
        secondBeat = false;
    }
}

//波形处理函数，放在定时器中执行，20ms执行一次
static void Ps_WaveformDeal(uint16_t adc_value)
{
    int16_t temp;
    uint16_t i = 0;

    temp = adc_value - 224;
    temp = 500 - temp;
    if (temp < 0)
        temp = 0;
    else if (temp > 500)
        temp = 500;
    temp = (uint8_t)(temp/2);

#if (COMMON_USE_LVGL == COMMON_OFF)
    /* 超过LCD显示范围之后从新开始 */
    for(i = 0; i < (PS_WAVE_POINT_NUM-1); i++)
    {
        ps_waveformlist[i] = ps_waveformlist[i+1];
    }
    ps_waveformlist[PS_WAVE_POINT_NUM-1] = temp;
#else
    /* 超过LCD显示范围之后从新开始 */
    for(i = 0; i < (PS_WAVE_POINT_NUM-1); i++)
    {
        ps_waveformlist[i].x = i;
        ps_waveformlist[i].y = ps_waveformlist[i+1].y;
    }
    ps_waveformlist[PS_WAVE_POINT_NUM-1].x = PS_WAVE_POINT_NUM-1;
    ps_waveformlist[PS_WAVE_POINT_NUM-1].y = temp;
#endif

    QS = 0;
}

/* 返回心率值 */
int Ps_GetBpm(void)
{
    return BPM;
}

bool Ps_GetQsFlg(void)
{
    return QS;
}

void Ps_ClrQsFlg(void)
{
    QS = 0;
}

/* 获取ADC采样值 */
uint16_t Ps_GetRawAdcValue(void)
{
    return (uint16_t)Signal;
}

/* 返回128个adc采样值 */
#if (COMMON_USE_LVGL == COMMON_OFF)
uint16_t* Ps_GetWaveformList(void)
#else
lv_point_t* Ps_GetWaveformList(void)
#endif
{
    return (&ps_waveformlist[0]);
}

///* 定时器1回调函数, 定时时间为1ms */
//void Timer1_CallbackFunc(timer_callback_args_t * p_args)
//{
//    static bool timer_2ms_ok = false;
//
//    if (TIMER_EVENT_CYCLE_END == p_args->event)
//    {
//        if (true == timer_2ms_ok)
//        {
//            /* Sample adc every 2ms */
//            Ps_ReadSampleValueFromAdc();
//            /* Calculate heart rate */
//            Ps_HeartRateDeal();
//        }
//
//        timer_2ms_ok = (timer_2ms_ok == false) ? (true) : (false);
//    }
//}


/* Initialize the Timer */
void Timer_Init(void)
{
    timer_oc_parameter_struct timer_ocintpara;
    timer_parameter_struct timer_initpara;

    rcu_periph_clock_enable(RCU_TIMER1);
    rcu_timer_clock_prescaler_config(RCU_TIMER_PSC_MUL4);

    timer_struct_para_init(&timer_initpara);
    timer_deinit(TIMER1);

    /* TIMER1 configuration */
    timer_initpara.prescaler         = 200-1;
    timer_initpara.alignedmode       = TIMER_COUNTER_EDGE;
    timer_initpara.counterdirection  = TIMER_COUNTER_UP;
    timer_initpara.period            = 2000;    /* 2ms */
    timer_initpara.clockdivision     = TIMER_CKDIV_DIV1;
    timer_initpara.repetitioncounter = 0;
    timer_init(TIMER1, &timer_initpara);

    /* auto-reload preload enable */
    timer_auto_reload_shadow_enable(TIMER1);
    /* Enable the interrupt for overflow */
    timer_interrupt_enable(TIMER1, TIMER_INT_UP);
    /* Configure the interrupt priority */
    nvic_irq_enable(TIMER1_IRQn, 5, 2);
    /* TIMER1 enable */
    timer_enable(TIMER1);
}

/* Timer interrupt service function */
void TIMER1_IRQHandler(void)
{
    if(SET == timer_interrupt_flag_get(TIMER1, TIMER_INT_UP))
    {
        /* Sample adc every 2ms */
        Ps_ReadSampleValueFromAdc();
        /* Calculate heart rate */
        Ps_HeartRateDeal();
        /* clear TIMER interrupt flag */
        timer_interrupt_flag_clear(TIMER1, TIMER_INT_UP);
    }
}


